iT邦幫忙

2022 iThome 鐵人賽

DAY 28
1

前言

本篇將延伸上一篇討論的L2 Regularization,延伸到所謂的Lp Regualrization,及其在Deep Learning中比較粗暴的作法。是作者本人覺得很喜歡的一個技術,不過實務上提昇通常很有限就是(汗)。

什麼是 Lp Regularization?

顧名思義,Lp Regularization,是利用Lp去進行Regularization的方式。而除了前一篇提過的L2是歐基理德距離以外,所謂的Lp指得便是線性代數中的Lp-norm

  • 可以看成是向量X在Lp空間底下的長度
  • 常見的L1就是絕對值加起來
  • 常見的L2則等同於常見的Mean Square、歐基理德距離

具體限制的方式一樣是把沒有開根號版本的Lp-norm加入loss,(通常是為了比較好微分、好算梯度、以及避免rounding error)再進行學習,可以寫成類似下面的概念式:

如此一來,除了可以達到對權重的限制以外,也可以根據p這個超參數的設置,進一步進行不同的限制效果。

具體實例可以參考上一篇也有提到Google爸爸的這個視覺化工具。裡面有L1跟L2的比較,大約總結底下幾項:

  1. L1由於使用絕對值,其性質通常會把不重要的係數降到0。會具有稀疏的性質,但這有時這又跟深度學習一堆隱藏層權重的性質有點衝突,所以通常比較少用一點。另外,在傳統迴歸當中,使用L1的方法又稱為LASSO(least absolute shrinkage and selection operator),shrinkage的名稱也從這邊來。
  2. L2的話,同時也近似於上一篇介紹的Weight Decay,由於使用Mean Square,數學性質好,也容易簡化,通常只是縮小跟限制大小,不太容易歸0。也因此最常被使用。傳統迴歸則叫做Ridge Regression。
  3. p不等於1與2的情況,我個人也沒有使用過。僅知道通常會分成p = 0, 0 < p < 1, p > 1三種情況會有大致不同的性質可以運用。有興趣的朋友可以試試,應該也有一些文獻可以查詢。
  4. 另外還有一種對L1與L2進行線性混合的regularization,叫做Elastic Regression,也有一些人使用,有興趣的朋友也可以試試看,但就不在今天實作的範圍內。

Weigth decay 與 L2 Regularization的差異

上一篇文章有提過,只要使用Optimizer中的weight decay就可以達到類似L2 Regularization的限制效果。但其實還有一個差異尚未提到的則是,由於weight decay是直接實作在整個梯度,因此整個Regularization的範圍是模型內所有的參數。傳統的迴歸僅作用在乘法項的權重,而不作用在截距項的BIAS,因此實際上還是不大一樣。

由於筆者是統計出身的,總覺得連帶截距項也懲罰很奇怪。因此本作為本篇文章的目標,決定自刻一個只作用在乘法項的權重的Lp Regularization實作。

實作方法

實作方法一樣可以透過增加lightning-module內的函數來作到,首先新增對應的超參數到hparma.yaml

train:
  weight_decay:
    p: 2
    lambda: 0.00001 # 0.01 * 0.001

然後新增計算lp power sum的函數:

def lp_power_sum(tensor, p):
    if p == 2:
        return (tensor ** 2).sum()
    else:
        return (tensor.abs() ** p).sum()

將利用lp power sum計算懲罰項的函數到增加lightning-module內:

def lp_penalty(self):
    reg_loss = 0
    for name, weight in self.named_parameters():
        if 'weight' in name:
            reg_loss += lp_power_sum(weight, p = self.CONFIG['train']['weight_decay']['p'])
    return reg_loss

最後更改train_step內計算loss的方式:

def training_step(self, batch: Any, batch_idx: int):
    inputs, preds, labels, loss = self.step(batch)
    self.log('train/loss', loss.item(), on_step=True, on_epoch=True, batch_size = inputs.shape[0])

    reg_loss = self.lp_norm()
    self.log('train/reg_loss', reg_loss.item(), on_step=True, on_epoch=True, batch_size = inputs.shape[0])
    reg_loss = self.lp_penalty()
    self.log('train/reg_psum_loss', reg_loss.item(), on_step=True, on_epoch=True, batch_size = inputs.shape[0])


    if self.CONFIG['train']['weight_decay']['lambda'] > 0:
        loss += reg_loss * self.CONFIG['train']['weight_decay']['lambda']
        self.log('train/total_loss', loss.item(), on_step=True, on_epoch=True, batch_size = inputs.shape[0])

    return loss

基本上就大功告成了!

另外解釋幾點:

  • 先前做的weight decay實驗只有紀錄lp norm 而沒有power sum,因此這邊仍然計算並紀錄lp_norm,純粹是希望log能與作比較。
  • lambda 選擇 0.00001是因為 0.02 / 2 (weight decay用0.02,但它的微分與直接L2會差一半,因此除二) * 0.001 (初始learning rate,避免adaptive gradient造成過於快速的參數下降)

實作結果

一樣請參考這個commit進行實驗。

跟前幾次相比的結果如下圖:

  • Lambda = 0.01 (沒有根據adaptive gradient去調整的Case)也有作,不過看起來似乎就是權重下降太快導致underfitting了
  • 雖然整體來看差異不大,但只在乘法項進行限制的L2 Regularization似乎達到了最好的表現,有點出乎意料。

本日小節

  • 實驗差點做不出來好險
  • Lp Regularization很浪漫,於是我還是決定實作

上一篇
[Day27] Weight Decay Regularization
下一篇
[Day29] Model Serving with Triton Inference Server
系列文
PyTorch 生態鏈實戰運用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言